package xyz.kail.demo;

import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.util.LinkedList;
import java.util.List;

/**
 * @see org.springframework.transaction.config.TxNamespaceHandler
 * @see org.springframework.transaction.config.TxAdviceBeanDefinitionParser
 */
@Configuration
public class MethodNameTransactionConfig {

    private static final String POINTCUT_EXPRESSION = "execution(* com.ttpai..service..*.*(..))";

    @Bean
    public DefaultBeanFactoryPointcutAdvisor defaultBeanFactoryPointcutAdvisor(PlatformTransactionManager transactionManager) {
        DefaultBeanFactoryPointcutAdvisor defaultBeanFactoryPointcutAdvisor = new DefaultBeanFactoryPointcutAdvisor();

        TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
        transactionInterceptor.setTransactionManager(transactionManager);
        transactionInterceptor.setTransactionAttributeSources(newTransactionAttributes());
        defaultBeanFactoryPointcutAdvisor.setAdvice(transactionInterceptor);


        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(POINTCUT_EXPRESSION);
        defaultBeanFactoryPointcutAdvisor.setPointcut(pointcut);


        return defaultBeanFactoryPointcutAdvisor;
    }

    /**
     * <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
     * <tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
     * <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
     * <tx:method name="del*" propagation="REQUIRED" rollback-for="Exception"/>
     * <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
     * <tx:method name="select*" read-only="true" propagation="SUPPORTS"/>
     * <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
     */
    private NameMatchTransactionAttributeSource[] newTransactionAttributes() {

        return new NameMatchTransactionAttributeSource[]{
                newNameMatchTransactionAttributeSource("select*", newReadTransactionAttribute()),
                newNameMatchTransactionAttributeSource("find*", newReadTransactionAttribute()),

                newNameMatchTransactionAttributeSource("save*", newWriteTransactionAttribute()),
                newNameMatchTransactionAttributeSource("insert*", newWriteTransactionAttribute()),
                newNameMatchTransactionAttributeSource("update*", newWriteTransactionAttribute()),
                newNameMatchTransactionAttributeSource("del*", newWriteTransactionAttribute()),
                newNameMatchTransactionAttributeSource("add*", newWriteTransactionAttribute()),
        };

    }

    private NameMatchTransactionAttributeSource newNameMatchTransactionAttributeSource(String methodName, TransactionAttribute attr) {
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        source.addTransactionalMethod(methodName, attr);
        return source;
    }


    /**
     * 读事务属性
     */
    private RuleBasedTransactionAttribute newReadTransactionAttribute() {
        RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();

        // 设定传播行为
        attribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);

        // 只读
        attribute.setReadOnly(true);

        return attribute;
    }

    /**
     * 写事务属性
     */
    private RuleBasedTransactionAttribute newWriteTransactionAttribute() {
        RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();

        // 设定传播行为
        attribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        // 设定回滚规则
        List<RollbackRuleAttribute> rollbackRules = new LinkedList<RollbackRuleAttribute>();
        rollbackRules.add(new RollbackRuleAttribute(Exception.class));
        attribute.setRollbackRules(rollbackRules);

        return attribute;
    }


}
